use v6;
unit module Python;


my $perl6-pyx;

INIT {
	$perl6-pyx = "/tmp/perl6-pyx-{$*PID}";
	spurt $perl6-pyx, q:to/EOF/
import sys;

def _log_impl(*args):
    for i in args:
        sys.stderr.write(str(i))
        sys.stderr.write(' ')
    sys.stderr.write("\n")

def _log_null(*args):
    pass

_log = _log_null
if len(sys.argv) == 2 and sys.argv[1] == '-l':
    _log = _log_impl;


def _do_loop():
    while True:
        xvars_ = dict();
        xvars_['cmd'] = sys.stdin.readline().rstrip()
        xvars_['res'] = 'None'
        try:
            xvars_['cmd'] = xvars_['cmd'].split(' ')
            xvars_['how'] = xvars_['cmd'][0]
            xvars_['txt'] = sys.stdin.read(int(xvars_['cmd'][1]) + 1)
            _log(">>>> ", xvars_['how'], xvars_['cmd'][1])
            xvars_['cm'] = compile(xvars_['txt'], '', xvars_['how'])
            if xvars_['how'] == 'eval':
                xvars_['res'] = repr(eval(xvars_['cm']))
                _log("<<<< ", xvars_['res'])
            else:
                exec(xvars_['cm'])
        except Exception as ex:
            _log("<<<< Exception:", ex)
            break

        sys.stdout.write(xvars_['res'])
        sys.stdout.write("\n")
        sys.stdout.flush()

try:
    _do_loop()
except Exception:
    pass

EOF
}

END {
	$perl6-pyx.IO.unlink
}

class python is export {
	has $!py;
	has $!run;
	has $!out;
	has $!log;
	
	submethod BUILD(:$!log) {
		my @params = @('/usr/bin/env', 'python3', $perl6-pyx);
		if $!log {
			@params.push: '-l';
		}
		$!py = Proc::Async.new(@params, :w);
		$!out = Supplier.new;
		$!py.stdout.tap: { $!out.emit: $_ }
		$!run = $!py.start;
	}

	method close() {
		$!py.close-stdin;
		await($!run).exitcode
	}

	method !talk(Str:D $txt, :$eval) {
		my $res = '';
		
		react {
			$!py.put: "{$eval ?? 'eval' !! 'exec'} {$txt.chars}";
			$!py.write: Blob.new($txt.encode.list);
			$!py.put: '';
	
			whenever $!out.Supply -> $n {
				if $!log {
					$*ERR.say: "==={$n}"
				}
				if $n.contains("\n") {
					$n ~~ m/^ \s* (.*?) \s* $/;
					$res ~= $0;
					done
				} else {
					$res ~= $n;
				}
			}
		}
		$res
	}
	
	method eval(Str:D $txt) {
		self!talk($txt, :eval);
	}

	method exec(Str:D $txt) {
		self!talk($txt);
		self
	}
}
